package com.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.entity.DataCheckColumnRuleBind;
import com.entity.DataCheckTableRuleBind;
import com.entity.dto.BindTableCheckRuleDto;
import com.entity.dto.BindTableColumnCheckRuleDto;
import com.entity.dto.QualityCheckRuleDto;
import com.entity.dto.TableCheck;
import com.entity.param.MySQLSchemaParam;
import com.entity.vo.DataColumnCheckExportVo;
import com.service.DataQualityService;
import com.service.MySqlSchemaService;
import com.util.AddressAnalysisUtil;
import com.util.JsonUtil;
import com.util.MessyCodeCheck;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import static com.constant.CommonConstants.RULES_KEY;

@Slf4j
@Service
public class DataQualityServiceImpl implements DataQualityService {
    @Resource
    private CacheManager cacheManager;

    @Resource
    private MySqlSchemaService mySqlSchemaService;

    @Override
    public Boolean bindTableCheckRules(List<BindTableCheckRuleDto> ruleDtos) {
        List<DataCheckTableRuleBind> tableRuleBinds = new LinkedList<>();
        ruleDtos.forEach(e->{
            DataCheckTableRuleBind tableRuleBind = new DataCheckTableRuleBind();
            tableRuleBind.setTableName(e.getTableName());
            tableRuleBind.setRuleCode(e.getRuleCode());
            tableRuleBinds.add(tableRuleBind);
        });
        String json = JsonUtil.toStr(tableRuleBinds);
        File file = new File("");
        String jsonPath = file.getAbsoluteFile() + File.separator + "rule" + File.separator + String.format("%s-rule.json",ruleDtos.get(0).getTableName());
        FileUtil.writeString(json,jsonPath,"utf-8");
        return Boolean.TRUE;
    }

    @Override
    public Boolean bindColumnCheckRules(List<BindTableColumnCheckRuleDto> ruleDtos) {
        List<DataCheckColumnRuleBind> rowRuleBinds = new LinkedList<>();
        ruleDtos.forEach(e->{
            DataCheckColumnRuleBind ruleBind = new DataCheckColumnRuleBind();
            ruleBind.setTableName(e.getTableName());
            ruleBind.setColumnName(e.getColumnName());
            ruleBind.setRuleCode(e.getRuleCode());
            rowRuleBinds.add(ruleBind);
        });
        String json = JsonUtil.toStr(rowRuleBinds);
        File file = new File("");
        String jsonPath = file.getAbsoluteFile() + File.separator + "rule" + File.separator + String.format("%s-%s.json",ruleDtos.get(0).getTableName(),
                ruleDtos.get(0).getColumnName());
        FileUtil.writeString(json,jsonPath,"utf-8");
        return Boolean.TRUE;
    }

    @Override
    public String doEmptyRate(List<Object> values) {
        int total=values.size();
        long count = values.stream()
                .filter(value -> value == null || value.toString().isEmpty())
                .count();
        double rate = NumberUtil.div(count, total);
        double rateValue = NumberUtil.mul(rate, 100);
        BigDecimal show = new BigDecimal(rateValue).setScale(2, RoundingMode.DOWN);
        String rateShow=show.doubleValue()+"%";
        return String.format("总条数：%s,空值数：%s,空值率：%s", total, count, rateShow);
    }

    @Override
    public String repeatRate(List<Object> values) {
        Map<Object, Long> countMap = values.stream()
                .collect(Collectors.groupingBy(o -> o, Collectors.counting()));
        Map<Object, Long> repeatMap = countMap.entrySet().stream()
                .filter(entry -> entry.getValue() > 1)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        double totalSize = values.size();
        if (CollectionUtil.isEmpty(countMap)){
            return "不存在重复值";
        }else {
            AtomicReference<String> repeatAll = new AtomicReference<>("");
            repeatMap.forEach((key, value) -> {
                double repeat = NumberUtil.div(value.doubleValue(), totalSize);
                double repeatValue = NumberUtil.mul(repeat, 100);
                BigDecimal show = new BigDecimal(repeatValue).setScale(2, RoundingMode.DOWN);
                String rateShow=show.doubleValue()+"%";
                repeatAll.updateAndGet(x->x+"{ 值: " + key + ", 重复数: " + value + ", 重复率: " + rateShow +"} \n");
            });
            return repeatAll.get();
        }
    }

    @Override
    public String lengthOk(List<Object> values,Integer okLength) {
        int total=values.size();
        long count = values.stream()
                .filter(Objects::nonNull)
                .filter(value -> StrUtil.isEmpty(value.toString()) || value.toString().length()!=okLength)
                .count();
        return String.format("总条数：%s,非法数：%s", total, count);
    }

    @Override
    public String garbledCount(List<Object> values) {
        int total=values.size();
        long count = values.stream()
                .filter(Objects::nonNull)
                .filter(value -> StrUtil.isNotEmpty(value.toString()) && MessyCodeCheck.isMessyCode(value.toString()))
                .count();
        return String.format("总条数：%s,乱码数：%s", total, count);
    }

    @Override
    public String valueRange(List<Object> values) {
        Map<Object, Long> collect = values.stream()
                .collect(Collectors.groupingBy(x -> x == null ? "空值" : x, Collectors.counting()));
        return collect.toString();
    }

    @Override
    public String addressRange(List<Object> values) {
        Map<Object, Long> collect = values.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.groupingBy(x -> AddressAnalysisUtil.convertDetailAddressToPCD(x.toString())[1], Collectors.counting()));
        return collect.toString();
    }

    @Override
    public void toCheck(List<DataColumnCheckExportVo> checkExportVoList, TableCheck e) {
        Map<String, List<Object>> tableColumnMap = new HashMap<>(16);
        MySQLSchemaParam param = new MySQLSchemaParam();
        param.setDatabaseName(e.getDataBase());
        param.setTableName(e.getTableName());
        Map<String, String> columnMap = mySqlSchemaService.columnCommentMap(param);
        List<Map<String, Object>> rows = mySqlSchemaService.queryList(param);
        log.info("{} ,按字段组装数据", e.getTableName());
        for (Map<String, Object> row : rows) {
            for (Map.Entry<String, Object> entry : row.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();

                if (tableColumnMap.containsKey(key)) {
                    tableColumnMap.get(key).add(value);
                } else {
                    List<Object> values = new ArrayList<>();
                    values.add(value);
                    tableColumnMap.put(key, values);
                }
            }
        }
        log.info("{} ,组装结束", e.getTableName());

        log.info("{} ,开始计算值", e.getTableName());
        tableColumnMap.forEach((k,v)->{
            DataColumnCheckExportVo exportVo = new DataColumnCheckExportVo();
            exportVo.setTableName(e.getTableName());
            exportVo.setColumnName(k);
            exportVo.setColumnComment(columnMap.get(k));
            //重复率计算
            exportVo.setRepeatRate(repeatRate(v));
            //空值率计算
            String emptyRate = doEmptyRate(v);
            exportVo.setEmptyRate(emptyRate);
            if (k.equals("TYSHXYDM")){
                String lengthOk = lengthOk(v,18);
                exportVo.setLengthOk(lengthOk);
            }
            if (k.equals("dwlxrgddh")){
                String lengthOk = lengthOk(v,11);
                exportVo.setLengthOk(lengthOk);
            }
            if (k.equals("dwlxrlxdh")){
                String lengthOk = lengthOk(v,13);
                exportVo.setLengthOk(lengthOk);
            }
            if (k.equals("uniscid")){
                String lengthOk = lengthOk(v,18);
                exportVo.setLengthOk(lengthOk);
            }
            //乱码率
            exportVo.setGarbledCount(garbledCount(v));
            //地址分布
            if (k.equals("record_address")){
                String range = addressRange(v);
                exportVo.setValueRange(range);
            }
            //值分布 ，分组值，各值数量
            if (k.equals("IS_ACTIVE")){
                exportVo.setValueRange(valueRange(v));
            }
            if (k.equals("QYLX")){
                exportVo.setValueRange(valueRange(v));
            }
            if (k.equals("buis_type_code")){
                exportVo.setValueRange(valueRange(v));
            }
            if (k.equals("move_type")){
                exportVo.setValueRange(valueRange(v));
            }
            if (k.equals("out_move")){
                exportVo.setValueRange(valueRange(v));
            }
            log.info("{} ,计算结束", e.getTableName());
            checkExportVoList.add(exportVo);
        });
    }

    @Override
    public void personCheck(List<DataColumnCheckExportVo> checkExportVoList, TableCheck e) {
        Map<String, List<Object>> tableColumnMap = new HashMap<>(16);
        MySQLSchemaParam param = new MySQLSchemaParam();
        param.setDatabaseName(e.getDataBase());
        param.setTableName(e.getTableName());
        Map<String, String> columnMap = mySqlSchemaService.columnCommentMap(param);
        List<Map<String, Object>> rows = mySqlSchemaService.queryList(param);
        log.info("{} ,按字段组装数据", e.getTableName());
        for (Map<String, Object> row : rows) {
            for (Map.Entry<String, Object> entry : row.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();

                if (tableColumnMap.containsKey(key)) {
                    tableColumnMap.get(key).add(value);
                } else {
                    List<Object> values = new ArrayList<>();
                    values.add(value);
                    tableColumnMap.put(key, values);
                }
            }
        }
        log.info("{} ,组装结束", e.getTableName());

        log.info("{} ,开始计算值", e.getTableName());
        tableColumnMap.forEach((k,v)->{
            DataColumnCheckExportVo exportVo = new DataColumnCheckExportVo();
            exportVo.setTableName(e.getTableName());
            exportVo.setColumnName(k);
            exportVo.setColumnComment(columnMap.get(k));
            //重复率计算
            exportVo.setRepeatRate(repeatRate(v));
            //空值率计算
            String emptyRate = doEmptyRate(v);
            exportVo.setEmptyRate(emptyRate);
            if (k.equals("TYSHXYDM")){
                String lengthOk = lengthOk(v,18);
                exportVo.setLengthOk(lengthOk);
            }
            if (k.equals("dwlxrgddh")){
                String lengthOk = lengthOk(v,11);
                exportVo.setLengthOk(lengthOk);
            }
            if (k.equals("dwlxrlxdh")){
                String lengthOk = lengthOk(v,13);
                exportVo.setLengthOk(lengthOk);
            }
            if (k.equals("uniscid")){
                String lengthOk = lengthOk(v,18);
                exportVo.setLengthOk(lengthOk);
            }
            //乱码率
            exportVo.setGarbledCount(garbledCount(v));
            //地址分布
            if (k.equals("record_address")){
                String range = addressRange(v);
                exportVo.setValueRange(range);
            }
            //值分布 ，分组值，各值数量
            if (k.equals("IS_ACTIVE")){
                exportVo.setValueRange(valueRange(v));
            }
            if (k.equals("QYLX")){
                exportVo.setValueRange(valueRange(v));
            }
            if (k.equals("buis_type_code")){
                exportVo.setValueRange(valueRange(v));
            }
            if (k.equals("move_type")){
                exportVo.setValueRange(valueRange(v));
            }
            if (k.equals("out_move")){
                exportVo.setValueRange(valueRange(v));
            }
            log.info("{} ,计算结束", e.getTableName());
            checkExportVoList.add(exportVo);
        });
    }

    /*@PostConstruct
    public void init(){
        Cache cache = cacheManager.getCache(RULES_KEY);
        List<QualityCheckRuleDto> ruleDtos = cache.get(RULES_KEY, List.class);
        if (CollectionUtil.isEmpty(ruleDtos)){
            log.info("-----数据质量检查,规则初始化-----");
            File file = new File("");
            String jsonPath = file.getAbsoluteFile() + File.separator + "rule" + File.separator + "default_rules.json";
            String ruleJson = FileUtil.readUtf8String(jsonPath);
            List<QualityCheckRuleDto> rules = JsonUtil.toList(ruleJson, QualityCheckRuleDto.class);
            cache.put(RULES_KEY,rules);
        }
    }*/

}
